/*********************************************************************************
 * Copyright(C),2015, xes peiyou
 * Author:      yaobyron@sina.com
 * Version:     2.0.0
 * Date:        20150615
 * Description:
        MFRC522模块驱动
 * History:
        Date            Author                  Modification
        20150601        yaobyron@sina.com       add comments
        20150615        yaobyron@sina.com       support buzzer
**********************************************************************************/

#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/platform_data/spi-rockchip.h>
#include <asm/uaccess.h>

#include "spi-rockchip-core.h"
#include "spi-rockchip-rc522.h"

#define SPI_RC522_MAJOR         153 /* assigned */
#define N_SPI_MINORS            32  /* ... up to 256 */
static DECLARE_BITMAP(minors, N_SPI_MINORS);

#define DEVICE_NAME             "spi_rc522"    /* device name, see it on /proc/devices */

#define VERSION                 "1.0.0"
#define DEBUG
#ifdef  DEBUG
    #define debug(fmt, args...) printk(fmt, ##args)
#else
    #define debug(fmt, args...)
#endif

#define SPI_DATA_BUF_SIZE       30 

struct rc522_card_data {
    volatile int            isready;
    volatile int            ishave;
    volatile int            poll_start;
    unsigned int            curkuain;
    unsigned char           selectedcard[6];
};

struct spi_rc522_data {
    dev_t                   devt;
    spinlock_t              spi_lock;
    struct spi_device       *spi;
    int                     reset_gpio;
    int                     buzzer_gpio;
    struct list_head        device_entry;
    struct workqueue_struct *rc522_wq;
    struct work_struct      rc522_work;
    struct workqueue_struct *buzzer_wq;
    struct work_struct      buzzer_work;
    wait_queue_head_t       poll_waitq;

    struct mutex            buf_lock;
    unsigned int            users;
	unsigned char           *rx_buf;
	int                     rx_len; 
	unsigned char           *tx_buf;
	int                     tx_len; 
    struct rc522_card_data  data;
    rc522_buzzer_t          buzzer;
};


static LIST_HEAD(device_list);
static DEFINE_MUTEX(device_list_lock);
static unsigned char defpasswd[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

/**************************** device *******************************/
unsigned char rc522_readrawrc(struct spi_rc522_data *spi_rc522, unsigned char addr)
{
    unsigned char ucaddr;

    ucaddr = ((addr << 1) & 0x7E) | 0x80;
    //spi_write(spi_rc522->spi, &ucaddr, 1);

    return spi_w8r8(spi_rc522->spi, ucaddr);
}

void rc522_writerawrc(struct spi_rc522_data *spi_rc522, unsigned char addr, unsigned char value)
{
    unsigned char ucaddr;
    unsigned char buf[2];

    ucaddr = (addr << 1) & 0x7E;
    
    buf[0] = ucaddr;
    buf[1] = value;
    spi_write(spi_rc522->spi, buf, 2);
    /*
    unsigned char ucaddr;
    unsigned char buf[2];

    struct spi_transfer st[2];  
    struct spi_message  msg; 

    ucaddr = (addr << 1) & 0x7E;

    spi_message_init(&msg);
    memset(st, 0, sizeof(st));

    st[0].tx_buf = &ucaddr;  
    st[0].len = 1;  
    spi_message_add_tail(&st[0], &msg);

    st[1].tx_buf = &value;  
    st[1].len = 1;  
    spi_message_add_tail(&st[1], &msg);  
    spi_sync(spi_rc522->spi, &msg);
    */
}

void rc522_setbitmask(struct spi_rc522_data *spi_rc522, unsigned char reg, unsigned char mask)
{
    unsigned char tmp = 0x0;

    tmp = rc522_readrawrc(spi_rc522, reg);
    rc522_writerawrc(spi_rc522, reg, tmp | mask);
}

void rc522_clearbitmask(struct spi_rc522_data *spi_rc522, unsigned char reg, unsigned char mask)
{
    unsigned char tmp = 0x0;

    tmp = rc522_readrawrc(spi_rc522, reg);
    rc522_writerawrc(spi_rc522, reg, tmp & ~mask);
}

void rc522_pcdantennaon(struct spi_rc522_data *spi_rc522)
{
    unsigned char val;
    
    val = rc522_readrawrc(spi_rc522, RC522_TxControlReg);
    if (!(val & 0x03))
    {
        rc522_setbitmask(spi_rc522, RC522_TxControlReg, 0x03);
    }
}

void rc522_pcdantennaoff(struct spi_rc522_data *spi_rc522)
{
    rc522_clearbitmask(spi_rc522, RC522_TxControlReg, 0x03);
}

void rc522_calulatecrc(struct spi_rc522_data *spi_rc522, unsigned char *pindata, unsigned char len, unsigned char *poutdata)
{
    unsigned char i, n;

    rc522_clearbitmask(spi_rc522, RC522_DivIrqReg, 0x04);
    rc522_writerawrc(spi_rc522, RC522_CommandReg, RC522_PCD_IDLE);
    rc522_setbitmask(spi_rc522, RC522_FIFOLevelReg, 0x80);
    for (i = 0; i < len; i++)
    {
        rc522_writerawrc(spi_rc522, RC522_FIFODataReg, *(pindata + i));
    }
    rc522_writerawrc(spi_rc522, RC522_CommandReg, RC522_PCD_CALCCRC);
    i = 0xFF;
    do {
        n = rc522_readrawrc(spi_rc522, RC522_DivIrqReg);
        i--;
    } while ((i != 0) && (n & 0x04));
    poutdata[0] = rc522_readrawrc(spi_rc522, RC522_CRCResultRegL);
    poutdata[1] = rc522_readrawrc(spi_rc522, RC522_CRCResultRegM);
}

char rc522_pcdreset(struct spi_rc522_data *spi_rc522)
{
    gpio_set_value(spi_rc522->reset_gpio, 1);
    mdelay(10);
    gpio_set_value(spi_rc522->reset_gpio, 0);
    mdelay(10);
    gpio_set_value(spi_rc522->reset_gpio, 1);
    mdelay(10);
    rc522_writerawrc(spi_rc522, RC522_CommandReg, RC522_PCD_RESETPHASE);
    mdelay(10);

    if(rc522_readrawrc(spi_rc522, 0x02) == 0x80)
    {
        debug(KERN_INFO "%s: reset...\n", DEVICE_NAME);

        rc522_writerawrc(spi_rc522, RC522_ModeReg, 0x3D); //和Mifare卡通讯，CRC初始值0x6363
        rc522_writerawrc(spi_rc522, RC522_TReloadRegL, 30);           
        rc522_writerawrc(spi_rc522, RC522_TReloadRegH, 0);
        rc522_writerawrc(spi_rc522, RC522_TModeReg, 0x8D);
        rc522_writerawrc(spi_rc522, RC522_TPrescalerReg, 0x3E);
        rc522_writerawrc(spi_rc522, RC522_TxAutoReg, 0x40);

        return RC522_MI_OK;
    }

    return RC522_MI_ERR;
}

char rc522_pcdconfigisotype(struct spi_rc522_data *spi_rc522, unsigned char type)
{
    if (type == 'A')
    {
        rc522_clearbitmask(spi_rc522, RC522_Status2Reg, 0x08);

        /*
        WriteRawRC(CommandReg,0x20);    //as default   
        WriteRawRC(ComIEnReg,0x80);     //as default
        WriteRawRC(DivlEnReg,0x0);      //as default
        WriteRawRC(ComIrqReg,0x04);     //as default
        WriteRawRC(DivIrqReg,0x0);      //as default
        WriteRawRC(Status2Reg,0x0);//80    //trun off temperature sensor
        WriteRawRC(WaterLevelReg,0x08); //as default
        WriteRawRC(ControlReg,0x20);    //as default
        WriteRawRC(CollReg,0x80);    //as default
        */

        rc522_writerawrc(spi_rc522, RC522_ModeReg, 0x3D); //3F

        /*
        WriteRawRC(TxModeReg,0x0);      //as default???
        WriteRawRC(RxModeReg,0x0);      //as default???
        WriteRawRC(TxControlReg,0x80);  //as default???

        WriteRawRC(TxSelReg,0x10);      //as default???
        */

        rc522_writerawrc(spi_rc522, RC522_RxSelReg, 0x86); // 84
        //WriteRawRC(RxThresholdReg,0x84);//as default
        //WriteRawRC(DemodReg,0x4D);      //as default
 
        //WriteRawRC(ModWidthReg,0x13);// 26
        rc522_writerawrc(spi_rc522, RC522_RFCfgReg, 0x7F); // 4F
        /*
        WriteRawRC(GsNReg,0x88);        //as default???
        WriteRawRC(CWGsCfgReg,0x20);    //as default???
        WriteRawRC(ModGsCfgReg,0x20);   //as default???
        */

        rc522_writerawrc(spi_rc522, RC522_TReloadRegL, 30);
        rc522_writerawrc(spi_rc522, RC522_TReloadRegH, 0);
        rc522_writerawrc(spi_rc522, RC522_TModeReg, 0x8D);
        rc522_writerawrc(spi_rc522, RC522_TPrescalerReg, 0x3E);

        mdelay(10);
        rc522_pcdantennaon(spi_rc522);
    } else {
        return RC522_MI_NOTAGERR;
    }

    return RC522_MI_OK;
}

int rc522_pcdcommf522(struct spi_rc522_data *spi_rc522, unsigned char cmd, unsigned char *pindata, unsigned char inlenbyte, unsigned char *poutdata, unsigned int *poutlenbit)
{
    char status = RC522_MI_ERR;
    unsigned char irqen   = 0x00;
    unsigned char waitfor = 0x00;
    unsigned char lastbits;
    unsigned char n;
    unsigned int i;

    switch (cmd)
    {
    case RC522_PCD_AUTHENT:
        irqen   = 0x12;
        waitfor = 0x10;
        break;
    case RC522_PCD_TRANSCEIVE:
        irqen   = 0x77;
        waitfor = 0x30;
        break;
    default:
        break;
    }

    rc522_writerawrc(spi_rc522, RC522_ComIEnReg, irqen | 0x80);
    rc522_clearbitmask(spi_rc522, RC522_ComIrqReg, 0x80);
    rc522_writerawrc(spi_rc522, RC522_CommandReg, RC522_PCD_IDLE);
    rc522_setbitmask(spi_rc522, RC522_FIFOLevelReg, 0x80);

    for (i=0; i<inlenbyte; i++)
    {   rc522_writerawrc(spi_rc522, RC522_FIFODataReg, pindata[i]);    }
    rc522_writerawrc(spi_rc522, RC522_CommandReg, cmd);


    if (cmd == RC522_PCD_TRANSCEIVE)
    {
        rc522_setbitmask(spi_rc522, RC522_BitFramingReg, 0x80);
    }

    i = 2000;
    do 
    {
        n = rc522_readrawrc(spi_rc522, RC522_ComIrqReg);
        i--;
    }
    while ((i != 0) && !(n & 0x01) && !(n & waitfor));

    rc522_clearbitmask(spi_rc522, RC522_BitFramingReg, 0x80);

    if (i != 0)
    {
        if(!(rc522_readrawrc(spi_rc522, RC522_ErrorReg) & 0x1B))
        {
            status = RC522_MI_OK;
            if (n & irqen & 0x01)
            {
                status = RC522_MI_NOTAGERR;
            }
            if (cmd == RC522_PCD_TRANSCEIVE)
            {
                n = rc522_readrawrc(spi_rc522, RC522_FIFOLevelReg);
                lastbits = rc522_readrawrc(spi_rc522, RC522_ControlReg) & 0x07;
                if (lastbits)
                {
                    *poutlenbit = (n - 1) * 8 + lastbits;
                } else {
                    *poutlenbit = n*8;
                }
                if (n == 0)
                {
                    n = 1;
                }
                if (n > RC522_MAXRXLEN)
                {
                    n = RC522_MAXRXLEN;
                }
                for (i = 0; i < n; i++)
                {
                    poutdata[i] = rc522_readrawrc(spi_rc522, RC522_FIFODataReg);
                }
            }
        }
        else
        {   status = RC522_MI_ERR;   }
    }

    rc522_setbitmask(spi_rc522, RC522_ControlReg, 0x80); // stop timer now
    rc522_writerawrc(spi_rc522, RC522_CommandReg, RC522_PCD_IDLE); 

    return status;
}

char rc522_pcdhalt(struct spi_rc522_data *spi_rc522)
{
    unsigned int unlen;
    unsigned char uccommf522buf[RC522_MAXRXLEN];

    uccommf522buf[0] = RC522_PICC_HALT;
    uccommf522buf[1] = 0;
    rc522_calulatecrc(spi_rc522, uccommf522buf, 2, &uccommf522buf[2]);

    rc522_pcdcommf522(spi_rc522, RC522_PCD_TRANSCEIVE, uccommf522buf, 4, uccommf522buf, &unlen);

    return RC522_MI_OK;
}

/*
 * Mifare Card Type:
 *  0x0004: MFOne-S50
 *  0x0002: MFOne-S70
 *  0x0044: MF-UltraLight
 *  0x0008: MF-Pro
 *  0x0344: MF-Desire
 */
char rc522_pcdrequest(struct spi_rc522_data *spi_rc522, unsigned char req_code, unsigned char *ptagtype)
{
    char status;
    unsigned int unlen;
    unsigned char uccommf522buf[RC522_MAXRXLEN];

    rc522_clearbitmask(spi_rc522, RC522_Status2Reg, 0x08);
    rc522_writerawrc(spi_rc522, RC522_BitFramingReg, 0x07);

    rc522_setbitmask(spi_rc522, RC522_TxControlReg, 0x03);

    uccommf522buf[0] = req_code;

    status = rc522_pcdcommf522(spi_rc522, RC522_PCD_TRANSCEIVE, uccommf522buf, 1, uccommf522buf, &unlen);

    if ((status == RC522_MI_OK) && (unlen == 0x10))
    {
        *ptagtype = uccommf522buf[0];
        *(ptagtype + 1) = uccommf522buf[1];
    } else {
        status = RC522_MI_ERR;
    }

    return status;
}

char rc522_pcdanticoll(struct spi_rc522_data *spi_rc522, unsigned char *psnr)
{
    char status;
    unsigned char i, snr_check = 0;
    unsigned int unlen;
    unsigned char uccommf522buf[RC522_MAXRXLEN];

    rc522_clearbitmask(spi_rc522, RC522_Status2Reg, 0x08);
    rc522_writerawrc(spi_rc522, RC522_BitFramingReg, 0x00);
    rc522_clearbitmask(spi_rc522, RC522_CollReg, 0x80);

    uccommf522buf[0] = RC522_PICC_ANTICOLL1;
    uccommf522buf[1] = 0x20;

    status = rc522_pcdcommf522(spi_rc522, RC522_PCD_TRANSCEIVE, uccommf522buf, 2, uccommf522buf, &unlen);

    if (status == RC522_MI_OK)
    {
        for ( i = 0; i < 4; i++)
        {
            *(psnr + i) = uccommf522buf[i];
            snr_check ^= uccommf522buf[i];
        }
        if (snr_check != uccommf522buf[i])
        {
            status = RC522_MI_ERR;
        }
    }

    rc522_setbitmask(spi_rc522, RC522_CollReg, 0x80);

    return status;
}

char rc522_pcdselect(struct spi_rc522_data *spi_rc522, unsigned char *psnr)
{
    char status;
    unsigned char i;
    unsigned int unlen;
    unsigned char uccommf522buf[RC522_MAXRXLEN];

    uccommf522buf[0] = RC522_PICC_ANTICOLL1;
    uccommf522buf[1] = 0x70;
    uccommf522buf[6] = 0;
    for ( i = 0; i < 4; i++)
    {
        uccommf522buf[i + 2] = *(psnr + i);
        uccommf522buf[6] ^= *(psnr + i);
    }
    rc522_calulatecrc(spi_rc522, uccommf522buf, 7, &uccommf522buf[7]);

    rc522_clearbitmask(spi_rc522, RC522_Status2Reg, 0x08);

    status = rc522_pcdcommf522(spi_rc522, RC522_PCD_TRANSCEIVE, uccommf522buf, 9, uccommf522buf, &unlen);

    if ((status == RC522_MI_OK) && (unlen == 0x18))
    {
        status = RC522_MI_OK;
    } else {
        status = RC522_MI_ERR;
    }

    return status;
}

char rc522_pcdauthstate(struct spi_rc522_data *spi_rc522, unsigned char auth_mode, unsigned char addr, unsigned char *pkey, unsigned char *psnr)
{
    char status;
    unsigned int unlen;
    unsigned char i, uccommf522buf[RC522_MAXRXLEN];

    uccommf522buf[0] = auth_mode;
    uccommf522buf[1] = addr;
    for (i = 0; i < 6; i++)
    {
        uccommf522buf[i + 2] = *(pkey + i);
    }
    for ( i = 0; i < 6; i++)
    {
        uccommf522buf[i + 8] = *(psnr + i);
    }

    status = rc522_pcdcommf522(spi_rc522, RC522_PCD_AUTHENT, uccommf522buf, 12, uccommf522buf, &unlen);
    if ((status != RC522_MI_OK) || (!(rc522_readrawrc(spi_rc522, RC522_Status2Reg) & 0x08)))
    {
        status = RC522_MI_ERR;
    }

    return status;
}

char rc522_pcdread(struct spi_rc522_data *spi_rc522, unsigned char addr, unsigned char *pdata)
{
    char status;
    unsigned int unlen;
    unsigned char i, uccommf522buf[RC522_MAXRXLEN];

    uccommf522buf[0] = RC522_PICC_READ;
    uccommf522buf[1] = addr;
    rc522_calulatecrc(spi_rc522, uccommf522buf, 2, &uccommf522buf[2]);

    status = rc522_pcdcommf522(spi_rc522, RC522_PCD_TRANSCEIVE, uccommf522buf, 4, uccommf522buf, &unlen);

    if ((status == RC522_MI_OK) && (unlen == 0x90))
    {
        for (i = 0; i < RC522_BLKDATA_SIZE; i++)
        {
            *(pdata + i) = uccommf522buf[i];
        }
    } else {
        status = RC522_MI_ERR;
    }

    return status;
}

char rc522_pcdwrite(struct spi_rc522_data *spi_rc522, unsigned char addr, unsigned char *pdata)
{
    char status;
    unsigned int unlen;
    unsigned char i, uccommf522buf[RC522_MAXRXLEN];

    uccommf522buf[0] = RC522_PICC_WRITE;
    uccommf522buf[1] = addr;
    rc522_calulatecrc(spi_rc522, uccommf522buf, 2, &uccommf522buf[2]);

    status = rc522_pcdcommf522(spi_rc522, RC522_PCD_TRANSCEIVE, uccommf522buf, 4, uccommf522buf, &unlen);

    if ((status != RC522_MI_OK) || (unlen != 4) || ((uccommf522buf[0] & 0x0F) != 0x0A))
    {
        status = RC522_MI_ERR;
    }

    if (status == RC522_MI_OK)
    {
        for (i = 0; i < RC522_BLKDATA_SIZE; i++)
        {
            uccommf522buf[i] = *(pdata + i);
        }
        rc522_calulatecrc(spi_rc522, uccommf522buf, 16, &uccommf522buf[16]);

        status = rc522_pcdcommf522(spi_rc522, RC522_PCD_TRANSCEIVE, uccommf522buf, 18, uccommf522buf, &unlen);
        if ((status != RC522_MI_OK) || (unlen != 4) || ((uccommf522buf[0] & 0x0F) != 0x0A))
        {
            status = RC522_MI_ERR;
        }
    }

    return status;
}
















/**************************** driver *******************************/
char rc522_init(struct spi_rc522_data *spi_rc522)
{
    if (rc522_pcdreset(spi_rc522) == RC522_MI_OK)
    {
        rc522_pcdantennaoff(spi_rc522);
        rc522_pcdantennaon(spi_rc522);
        rc522_pcdconfigisotype(spi_rc522, 'A');

        spi_rc522->data.isready = 1;

        return RC522_MI_OK;
    } else {
        spi_rc522->data.isready = 0;
    }

	debug(KERN_INFO "%s: %s rc522_init error!\n", DEVICE_NAME, __func__);
    return RC522_MI_ERR;
}

static void rc522_loop_work(struct work_struct *work)
{
    struct spi_rc522_data *spi_rc522;
    char status;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    spi_rc522 = container_of(work, struct spi_rc522_data, rc522_work);

    while (spi_rc522->data.isready && spi_rc522->data.poll_start)
    {
        msleep(50);
        // 寻天线区内未进入休眠状态的卡，返回卡片类型2字节
        status = rc522_pcdrequest(spi_rc522, RC522_PICC_REQIDL, spi_rc522->data.selectedcard);
        if (status != RC522_MI_OK)
        {
            continue;
        }
        /*
        // 防冲撞，返回卡的序列号4字节
        status = rc522_pcdanticoll(spi_rc522, &spi_rc522->data.selectedcard[2]);
        if (status != RC522_MI_OK)
        {
            printk(KERN_WARNING "%s: %s get card: no number\n", DEVICE_NAME, __func__);
            continue;
        }
        */

        spi_rc522->data.ishave = 1;
        spi_rc522->data.poll_start = 0;
        wake_up_interruptible(&spi_rc522->poll_waitq);
    }
}

static void buzzer_loop_work(struct work_struct *work)
{
    struct spi_rc522_data *spi_rc522;
    int i;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    spi_rc522 = container_of(work, struct spi_rc522_data, buzzer_work);

    if (spi_rc522->data.isready)
    {
        for (i = 0; i < spi_rc522->buzzer.times; i++)
        {
            gpio_set_value(spi_rc522->buzzer_gpio, 1);
            msleep(spi_rc522->buzzer.timeout);
            gpio_set_value(spi_rc522->buzzer_gpio, 0);
            if (i < spi_rc522->buzzer.times)
                msleep(spi_rc522->buzzer.timeout);
        }
    }
}

/* Read-only message with current device setup */
static ssize_t
spi_rc522_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    struct spi_rc522_data *spi_rc522;
    ssize_t ret = 0;
    char status;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    spi_rc522 = filp->private_data;

    status = rc522_pcdauthstate(spi_rc522, RC522_PICC_AUTHENT1A,
            spi_rc522->data.curkuain, defpasswd, &spi_rc522->data.selectedcard[2]);
    if (status != RC522_MI_OK)
    {
        printk(KERN_WARNING "%s: %s read authorize card err\n", DEVICE_NAME, __func__);
        ret = -ENXIO;
        goto ret;
    }

    mutex_lock(&spi_rc522->buf_lock);
    status = rc522_pcdread(spi_rc522, spi_rc522->data.curkuain, spi_rc522->rx_buf);
    if (status != RC522_MI_OK)
    {
        spi_rc522->rx_len = 0;
        printk(KERN_WARNING "%s: %s read card err\n", DEVICE_NAME, __func__);
        ret = -ENXIO;
        mutex_unlock(&spi_rc522->buf_lock);
        goto ret;
    } else {
        spi_rc522->rx_len = RC522_BLKDATA_SIZE;

#if 0
        /* debug */
        int i;
        debug(KERN_WARNING "%s: %s read block %d info:\n", DEVICE_NAME, __func__, spi_rc522->data.curkuain);
        for (i = 0; i < RC522_BLKDATA_SIZE; i++)
        {
            debug(KERN_INFO "%2.2X ", spi_rc522->rx_buf[i]);
        }
        debug(KERN_INFO "\n");
#endif
    }

    if (copy_to_user(buf, spi_rc522->rx_buf, spi_rc522->rx_len))
    {
        ret = -EFAULT;
    } else {
        ret = spi_rc522->rx_len;
    }
    mutex_unlock(&spi_rc522->buf_lock);

ret:
    return ret;
}

/* Write-only message with current device setup */
static ssize_t
spi_rc522_write(struct file *filp, const char __user *buf,
        size_t count, loff_t *f_pos)
{
    struct spi_rc522_data *spi_rc522;
    ssize_t ret = 0;
    char status;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    /* chipselect only toggles at start or end of operation */
    if (count != RC522_BLKDATA_SIZE)
    {
        return -EMSGSIZE;
    }

    spi_rc522 = filp->private_data;

    mutex_lock(&spi_rc522->buf_lock);
    if (copy_from_user(spi_rc522->tx_buf, buf, count) == 0)
    {
        spi_rc522->tx_len = count;
        status = rc522_pcdauthstate(spi_rc522, RC522_PICC_AUTHENT1A,
                spi_rc522->data.curkuain, defpasswd, &spi_rc522->data.selectedcard[2]);
        if (status != RC522_MI_OK)
        {
            printk(KERN_WARNING "%s: %s write authorize card err\n", DEVICE_NAME, __func__);
            ret = -ENXIO;
            mutex_unlock(&spi_rc522->buf_lock);
            goto ret;
        }

        status = rc522_pcdwrite(spi_rc522, spi_rc522->data.curkuain, spi_rc522->tx_buf);
        if (status != RC522_MI_OK)
        {
            printk(KERN_WARNING "%s: %s write card err\n", DEVICE_NAME, __func__);
            ret = -ENXIO;
            mutex_unlock(&spi_rc522->buf_lock);
            goto ret;
        }
#if 0
        else {
            /* debug */
            int i;
            debug(KERN_WARNING "%s: %s write block %d info:\n", DEVICE_NAME, __func__, spi_rc522->data.curkuain);
            for (i = 0; i < RC522_BLKDATA_SIZE; i++)
            {
                debug(KERN_INFO "%2.2X ", spi_rc522->tx_buf[i]);
            }
            debug(KERN_INFO "\n");
        }
#endif

        ret = spi_rc522->tx_len;
    } else {
        ret = -EFAULT;
    }

    mutex_unlock(&spi_rc522->buf_lock);

ret:
    return ret;
}

static unsigned int
spi_rc522_poll(struct file *filp, poll_table *wait)
{
    unsigned int mask = 0;
    struct spi_rc522_data *spi_rc522;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    spi_rc522 = filp->private_data;

    poll_wait(filp, &spi_rc522->poll_waitq, wait);

    if (spi_rc522->data.ishave)
    {
        // read function set ishave clear
        debug(KERN_INFO "%s: %s have card\n", DEVICE_NAME, __func__);
        spi_rc522->data.ishave = 0;
        mask |= POLLIN | POLLRDNORM;
        mask |= POLLOUT | POLLWRNORM;
    }

    return mask;
}

static long
spi_rc522_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int err = 0;
    int retval = 0;
    struct spi_rc522_data *spi_rc522;
    char status;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    /* Check type and command number */
    if (_IOC_TYPE(cmd) != RC522_IOC_MAGIC)
        return -EINVAL;
    if (_IOC_NR(cmd) > RC522_IOC_MAXNR)
        return -EINVAL;

    /* Check access direction once here; don't repeat below.
     * IOC_DIR is from the user perspective, while access_ok is
     * from the kernel perspective; so they look reversed.
     */
    if (_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE,
                (void __user *)arg, _IOC_SIZE(cmd));
    if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ,
                (void __user *)arg, _IOC_SIZE(cmd));
    if (err)
        return -EFAULT;

    /* guard against device removal before, or while,
     * we issue this ioctl.
     */
    spi_rc522 = filp->private_data;

    if (spi_rc522->data.isready == 0)
        return -ENODEV;

    /* use the buffer lock here for triple duty:
     *  - prevent I/O (from us) so calling spi_setup() is safe;
     *  - prevent concurrent SPI_IOC_WR_* from morphing
     *    data fields while SPI_IOC_RD_* reads them;
     *  - SPI_IOC_MESSAGE needs the buffer locked "normally".
     */
    mutex_lock(&spi_rc522->buf_lock);

    switch (cmd) {
    case RC522_CMD_ISREADY:
        debug(KERN_INFO "%s: %s card is ready: %d\n", DEVICE_NAME, __func__, spi_rc522->data.isready);
        if (spi_rc522->data.isready != 1)
            retval = -ENODEV;
        break;
    case RC522_CMD_SELECTCARD:
        debug(KERN_INFO "%s: %s select card\n", DEVICE_NAME, __func__);
        /*
        status = rc522_pcdrequest(spi_rc522, RC522_PICC_REQIDL, spi_rc522->data.selectedcard);
        if (status != RC522_MI_OK)
        {
            printk(KERN_WARNING "%s: %s request card error\n", DEVICE_NAME, __func__);
            retval = -ENXIO;
            break;
        }
        */
        // 防冲撞，返回卡的序列号4字节
        status = rc522_pcdanticoll(spi_rc522, &spi_rc522->data.selectedcard[2]);
        if (status != RC522_MI_OK)
        {
            printk(KERN_WARNING "%s: %s get card: no number\n", DEVICE_NAME, __func__);
            retval = -ENXIO;
            break;
        }
        // 选卡
        status = rc522_pcdselect(spi_rc522, &spi_rc522->data.selectedcard[2]);
        if (status != RC522_MI_OK)
        {
            printk(KERN_WARNING "%s: %s select card: no card\n", DEVICE_NAME, __func__);
            retval = -ENXIO;
            break;
        }

        if (copy_to_user((char *)arg, spi_rc522->data.selectedcard, sizeof(spi_rc522->data.selectedcard))) {
            printk("%s: %s copy card number to userspace err\n", DEVICE_NAME, __func__);
            retval = -EFAULT;
            break;
        }
        printk(KERN_INFO "%s: %s select card type: %02X%02X sn: %02X%02X%02X%02X\n", DEVICE_NAME, __func__,
                spi_rc522->data.selectedcard[1], spi_rc522->data.selectedcard[0],
                spi_rc522->data.selectedcard[5], spi_rc522->data.selectedcard[4], spi_rc522->data.selectedcard[3], spi_rc522->data.selectedcard[2]);
        break;
    case RC522_CMD_CHANGEBLK:
        debug(KERN_INFO "%s: %s change block number %ld\n", DEVICE_NAME, __func__, arg);
        spi_rc522->data.curkuain = (int)arg;
        break;
    case RC522_CMD_RESETCARD:
        debug(KERN_INFO "%s: %s reset card\n", DEVICE_NAME, __func__);
        spi_rc522->data.ishave = 0;
        spi_rc522->data.poll_start = 0;
        if (rc522_init(spi_rc522) == RC522_MI_ERR)
            retval = -ENODEV;
        break;
    case RC522_CMD_HALTCARD:
        debug(KERN_INFO "%s: %s halt card\n", DEVICE_NAME, __func__);
        rc522_pcdhalt(spi_rc522);
        break;
    case RC522_CMD_POLL:
        spi_rc522->data.poll_start = 1;
        if (!work_busy(&spi_rc522->rc522_work))
            queue_work(spi_rc522->rc522_wq, &spi_rc522->rc522_work);
        break;
    case RC522_CMD_BUZZER:
        if(copy_from_user(&spi_rc522->buzzer, (rc522_buzzer_t *)arg, sizeof(rc522_buzzer_t))){
            retval = - EFAULT;
        } else {
            if (!work_busy(&spi_rc522->rc522_work))
                queue_work(spi_rc522->buzzer_wq, &spi_rc522->buzzer_work);
        }
        break;
    default:
        break;
    }

    mutex_unlock(&spi_rc522->buf_lock);
    /*
    spi_dev_put(spi);
    */

    return retval;
}

#ifdef CONFIG_COMPAT
static long
spi_rc522_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    return spi_rc522_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#else
#define spi_rc522_compat_ioctl NULL
#endif /* CONFIG_COMPAT */

static int spi_rc522_open(struct inode *inode, struct file *filp)
{
    struct spi_rc522_data *spi_rc522;
    int status = -ENXIO;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    mutex_lock(&device_list_lock);

    list_for_each_entry(spi_rc522, &device_list, device_entry) {
        if (spi_rc522->devt == inode->i_rdev) {
            status = 0;
            break;
        }
    }
    if (status == 0) {
        // signal user check
        if (spi_rc522->users > 0)
        {
            printk(KERN_WARNING "%s: device has been opened!\n", DEVICE_NAME);
            mutex_unlock(&device_list_lock);

            return -EBUSY;
        }

        if (!spi_rc522->rx_buf) {
            spi_rc522->rx_buf = kmalloc(SPI_DATA_BUF_SIZE, GFP_KERNEL);
            if (!spi_rc522->rx_buf) {
                dev_err(&spi_rc522->spi->dev, "open/ENOMEM\n");
                status = -ENOMEM;
            }
        }
        if (!spi_rc522->tx_buf) {
            spi_rc522->tx_buf = kmalloc(SPI_DATA_BUF_SIZE, GFP_KERNEL);
            if (!spi_rc522->tx_buf) {
                dev_err(&spi_rc522->spi->dev, "open/ENOMEM\n");
                status = -ENOMEM;
            }
        }
        if (status == 0) {
            spi_rc522->users++;
            debug(KERN_INFO "%s: users%d\n", DEVICE_NAME, spi_rc522->users);
            filp->private_data = spi_rc522;
            nonseekable_open(inode, filp);
        }
    } else
    {
        pr_debug("spi_rc522: nothing for minor %d\n", iminor(inode));
    }

    mutex_unlock(&device_list_lock);

    rc522_init(spi_rc522);

    /*
    if (!work_busy(&spi_rc522->rc522_work))
        queue_work(spi_rc522->rc522_wq, &spi_rc522->rc522_work);
    */

    return status;
}

static int spi_rc522_release(struct inode *inode, struct file *filp)
{
    struct spi_rc522_data *spi_rc522;
    int status = 0;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    mutex_lock(&device_list_lock);
    spi_rc522 = filp->private_data;
    filp->private_data = NULL;

    /* last close? */
    spi_rc522->users--;
    if (!spi_rc522->users) {
        int        dofree;

        kfree(spi_rc522->tx_buf);
        spi_rc522->tx_buf = NULL;
        spi_rc522->tx_len = 0;
        kfree(spi_rc522->rx_buf);
        spi_rc522->rx_buf = NULL;
        spi_rc522->rx_len = 0;

        /* ... after we unbound from the underlying device? */
        spin_lock_irq(&spi_rc522->spi_lock);
        dofree = (spi_rc522->spi == NULL);
        spin_unlock_irq(&spi_rc522->spi_lock);

        if (dofree)
        {
            gpio_free(spi_rc522->reset_gpio);
            gpio_free(spi_rc522->buzzer_gpio);
            kfree(spi_rc522);
        }
    }
    mutex_unlock(&device_list_lock);

    return status;
}

static const struct file_operations spi_rc522_fops = {
    .owner =    THIS_MODULE,
    /* REVISIT switch to aio primitives, so that userspace
     * gets more complete API coverage.  It'll simplify things
     * too, except for the locking.
     */
    .write          = spi_rc522_write,
    .read           = spi_rc522_read,
    .poll           = spi_rc522_poll,
    .unlocked_ioctl = spi_rc522_ioctl,
    .compat_ioctl   = spi_rc522_compat_ioctl,
    .open           = spi_rc522_open,
    .release        = spi_rc522_release,
    .llseek         = no_llseek,
};

/*-------------------------------------------------------------------------*/

/* The main reason to have this class is to make mdev/udev create the
 * /dev/spi_rc522B.C character device nodes exposing our userspace API.
 * It also simplifies memory management.
 */

static struct class *spi_rc522_class;


#ifdef CONFIG_OF
static struct dw_spi_chip *spi_parse_dt(struct spi_rc522_data *spi_rc522)
{
	u32 temp;
	struct dw_spi_chip *spi_chip_data; 
    struct device *dev = &spi_rc522->spi->dev;
    int gpio;
	
	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

	spi_chip_data = devm_kzalloc(dev, sizeof(*spi_chip_data), GFP_KERNEL);
	if (!spi_chip_data) {
		dev_err(dev, "memory allocation for spi_chip_data failed\n");
		return ERR_PTR(-ENOMEM);
	}
	
	if (of_property_read_u32(dev->of_node, "poll_mode", &temp)) {
		dev_warn(dev, "fail to get poll_mode, default set 0\n");
		spi_chip_data->poll_mode = 0;
	} else {
		spi_chip_data->poll_mode = temp;
	}

	if (of_property_read_u32(dev->of_node, "type", &temp)) {
		dev_warn(dev, "fail to get type, default set 0\n");
		spi_chip_data->type = 0;
	} else {
		spi_chip_data->type = temp;
	}

	if (of_property_read_u32(dev->of_node, "enable_dma", &temp)) {
		dev_warn(dev, "fail to get enable_dma, default set 0\n");
		spi_chip_data->enable_dma = 0;
	} else {
		spi_chip_data->enable_dma = temp;
	}

    // reset-gpio
    gpio = of_get_named_gpio_flags(dev->of_node, "reset-gpio", 0, &temp);
    if (!gpio_is_valid(gpio)) {
		dev_err(dev, "fail to get the valid reset-gpio\n");
        goto reset_gpio_error;
    }
    if (gpio_request(gpio, "spi_rc522_reset")) {
		dev_err(dev, "fail to get the valid reset-gpio\n");
        goto reset_gpio_error;
    }
    spi_rc522->reset_gpio = gpio;
    temp = (temp == OF_GPIO_ACTIVE_LOW) ? 0 : 1;
    gpio_direction_output(spi_rc522->reset_gpio, temp);

    // buzzer-gpio
    gpio = of_get_named_gpio_flags(dev->of_node, "buzzer-gpio", 0, &temp);
    if (!gpio_is_valid(gpio)) {
		dev_err(dev, "fail to get the valid buzzer-gpio\n");
        goto buzzer_gpio_error;
    }
    if (gpio_request(gpio, "spi_rc522_reset")) {
		dev_err(dev, "fail to get the valid buzzer-gpio\n");
        goto buzzer_gpio_error;
    }
    spi_rc522->buzzer_gpio = gpio;
    temp = (temp == OF_GPIO_ACTIVE_LOW) ? 0 : 1;
    gpio_direction_output(spi_rc522->buzzer_gpio, temp);

	return spi_chip_data;

buzzer_gpio_error:
    gpio_free(spi_rc522->reset_gpio);
reset_gpio_error:
    return ERR_PTR(-ENODEV);
}
#else
static struct spi_board_info *spi_parse_dt(struct spi_rc522_data *spi_rc522)
{
	return spi_rc522->spi->dev->platform_data;
}
#endif


static int spi_rc522_probe(struct spi_device *spi)
{
	static struct dw_spi_chip *spi_chip_data = NULL;
    struct spi_rc522_data *spi_rc522 = NULL;
    int status;
    unsigned long minor;

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

	if(!spi)	
        return -EINVAL;

    /* Allocate driver data */
    spi_rc522 = (struct spi_rc522_data *)kzalloc(sizeof(struct spi_rc522_data), GFP_KERNEL);
    if (!spi_rc522)
    {
		dev_err(&spi->dev, "ERR: no memory for spi_rc522_data\n");
        return -ENOMEM;
    }
    spi_rc522->spi = spi;

	if (!spi_chip_data && spi->dev.of_node) {
		spi_chip_data = spi_parse_dt(spi_rc522);
		if (IS_ERR(spi_chip_data))
        {
            kfree(spi_rc522);
            return -EINVAL;
        }
	}

    /* Initialize the driver data */
    spi->bits_per_word = 8;
    spi->controller_data = spi_chip_data;
    spin_lock_init(&spi_rc522->spi_lock);
    mutex_init(&spi_rc522->buf_lock);

    INIT_LIST_HEAD(&spi_rc522->device_entry);

    /* If we can allocate a minor number, hook up this device.
     * Reusing minors is fine so long as udev or mdev is working.
     */
    mutex_lock(&device_list_lock);
    minor = find_first_zero_bit(minors, N_SPI_MINORS);
    if (minor < N_SPI_MINORS) {
        struct device *dev;

        spi_rc522->devt = MKDEV(SPI_RC522_MAJOR, minor);
        dev = device_create(spi_rc522_class, &spi->dev, spi_rc522->devt,
                    spi_rc522, "spi_rc522%d.%d",
                    spi->master->bus_num, spi->chip_select);
        status = PTR_RET(dev);
    } else {
        dev_dbg(&spi->dev, "no minor number available!\n");
        status = -ENODEV;
    }
    if (status == 0) {
        set_bit(minor, minors);
        list_add(&spi_rc522->device_entry, &device_list);
    }
    mutex_unlock(&device_list_lock);

    if (status == 0)
    {
        status = spi_setup(spi);
        spi_set_drvdata(spi, spi_rc522);

        /* spi_rc522 init start*/
        spi_rc522->rc522_wq = create_singlethread_workqueue("spi_rc522_work");
        spi_rc522->buzzer_wq = create_singlethread_workqueue("spi_rc522_buzzer_work");
        INIT_WORK(&spi_rc522->rc522_work, rc522_loop_work);
        INIT_WORK(&spi_rc522->buzzer_work, buzzer_loop_work);

        init_waitqueue_head(&spi_rc522->poll_waitq);
        // 默认读第一块
        spi_rc522->data.curkuain = 1;
        /* spi_rc522 init end*/

        printk(KERN_INFO "%s:name=%s,bus_num=%d,cs=%d,mode=%d,speed=%d\n",__func__,spi->modalias, spi->master->bus_num, spi->chip_select, spi->mode, spi->max_speed_hz);
        printk(KERN_INFO "%s:poll_mode=%d, type=%d, enable_dma=%d\n",__func__, spi_chip_data->poll_mode, spi_chip_data->type, spi_chip_data->enable_dma);
    }
    else
    {
        gpio_free(spi_rc522->reset_gpio);
        gpio_free(spi_rc522->buzzer_gpio);
        kfree(spi_rc522);
        debug(KERN_INFO "%s: %s fail!\n", DEVICE_NAME, __func__);
    }

    return status;
}

static int spi_rc522_remove(struct spi_device *spi)
{
    struct spi_rc522_data *spi_rc522 = spi_get_drvdata(spi);

	debug(KERN_INFO "%s: %s\n", DEVICE_NAME, __func__);

    /* make sure ops on existing fds can abort cleanly */
    spin_lock_irq(&spi_rc522->spi_lock);
    spi_rc522->spi = NULL;
    spi_set_drvdata(spi, NULL);
    spin_unlock_irq(&spi_rc522->spi_lock);

    /* prevent new opens */
    mutex_lock(&device_list_lock);
    list_del(&spi_rc522->device_entry);
    device_destroy(spi_rc522_class, spi_rc522->devt);
    clear_bit(MINOR(spi_rc522->devt), minors);
    if (spi_rc522->users == 0)
    {
        spi_rc522->data.poll_start = 0;
        flush_work(&spi_rc522->rc522_work);
        flush_work(&spi_rc522->buzzer_work);
        //cancel_work_sync(&spi_rc522->rc522_work);
        destroy_workqueue(spi_rc522->rc522_wq);
        destroy_workqueue(spi_rc522->buzzer_wq);

        gpio_free(spi_rc522->reset_gpio);
        gpio_free(spi_rc522->buzzer_gpio);
        kfree(spi_rc522);
    }
    mutex_unlock(&device_list_lock);

    return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id spi_rc522_dt_ids[] = {
	{ .compatible = "xes,spi_rc522_bus0_cs0", },
    {},
};
#endif /* CONFIG_OF */

MODULE_DEVICE_TABLE(of, spi_rc522_dt_ids);

static struct spi_driver spi_rc522_spi_driver = {
    .driver = {
        .name =             "spi_rc522",
        .owner =            THIS_MODULE,
        .of_match_table =   of_match_ptr(spi_rc522_dt_ids),
    },
    .probe =        spi_rc522_probe,
    .remove =       spi_rc522_remove,

    /* NOTE:  suspend/resume methods are not necessary here.
     * We don't do anything except pass the requests to/from
     * the underlying controller.  The refrigerator handles
     * most issues; the controller driver handles the rest.
     */
};

/*-------------------------------------------------------------------------*/

static int __init spi_rc522_init(void)
{
    int status;

    /* Claim our 256 reserved device numbers.  Then register a class
     * that will key udev/mdev to add/remove /dev nodes.  Last, register
     * the driver which manages those device numbers.
     */
    BUILD_BUG_ON(N_SPI_MINORS > 256);
    status = register_chrdev(SPI_RC522_MAJOR, "spi", &spi_rc522_fops);
    if (status < 0)
        return status;

    spi_rc522_class = class_create(THIS_MODULE, "spi_rc522");
    if (IS_ERR(spi_rc522_class)) {
        unregister_chrdev(SPI_RC522_MAJOR, spi_rc522_spi_driver.driver.name);
        return PTR_ERR(spi_rc522_class);
    }

    status = spi_register_driver(&spi_rc522_spi_driver);
    if (status < 0) {
        class_destroy(spi_rc522_class);
        unregister_chrdev(SPI_RC522_MAJOR, spi_rc522_spi_driver.driver.name);
    }

    return status;
}
module_init(spi_rc522_init);

static void __exit spi_rc522_exit(void)
{
    spi_unregister_driver(&spi_rc522_spi_driver);
    class_destroy(spi_rc522_class);
    unregister_chrdev(SPI_RC522_MAJOR, spi_rc522_spi_driver.driver.name);
}
module_exit(spi_rc522_exit);

MODULE_AUTHOR("Byron <yaobyron@sina.com>");
MODULE_DESCRIPTION("RC522 SPI Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:spi_rc522");
